﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using LAIR.ResourceAPIs.WordNet;

namespace CBTM.Processor
{
    public static class Misc
    {
        private static bool _customConverge = false;

        /// <summary>
        /// Duyệt theo chiều sâu, dùng cho quá trình tìm hyponym của class code
        /// </summary>
        /// <param name="synset"></param>
        /// <param name="classcodes"></param>
        /// <param name="writer"></param>
        public static void Traverse(SynSet synset, List<int> classcodes, StreamWriter writer)
        {
            foreach (SynSet s in synset.GetRelatedSynSets(WordNetEngine.SynSetRelation.Hyponym, false))
                // chỉ chọn các hyponym không phải class code
                if (!classcodes.Contains(s.Offset))
                {
                    writer.Write(s.Offset + ";");
                    // đi tới các hyponym của hyponym đang xét
                    Traverse(s, classcodes, writer);
                }
        }

        /// <summary>
        /// Trả về class code của synset
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="sc_table"></param>
        /// <returns></returns>
        public static string GetClassCode(string offset, Dictionary<string, string> sc_table)
        {
            string classcode = string.Empty;
            sc_table.TryGetValue(offset, out classcode);
            return classcode;
        }

        /// <summary>
        /// Tạo danh sách stop word
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static List<string> LoadStopWords(string file)
        {
            List<string> lst = new List<string>();
            using (StreamReader sr = new StreamReader(file))
            {
                while (sr.Peek() >= 0)
                    lst.Add(sr.ReadLine());
            }
            Console.WriteLine("Stop word list loaded.");
            return lst;
        }

        /// <summary>
        /// Trả về unigram và bigram chồng của từ tiếng Việt - dùng để tính xác suất ngram
        /// </summary>
        /// <param name="sc_table"></param>
        /// <param name="stoplist"></param>
        /// <param name="p"></param>
        /// <param name="pug"></param>
        /// <param name="pbg"></param>
        public static void GetFreqList(Dictionary<string, string> sc_table, List<string> stoplist, Dictionary<string, float> p, out Dictionary<string, Dictionary<string, float>> pug, out Dictionary<string, Dictionary<string, float>> pbg)
        {
            // lập danh sách unigram và bigram
            pug = new Dictionary<string, Dictionary<string, float>>();//unigram
            pbg = new Dictionary<string, Dictionary<string, float>>();//bigram chồng
            float countUni = 0, countBi = 0;// đếm số lượng unigram và bigram
            foreach (var kvp in p)
            {
                string[] args = kvp.Key.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                string g = GetClassCode(args[2], sc_table);
                //if (g == string.Empty) continue;
                if (g == null) continue;
                string[] arrUnigram = args[0].Split('_');
                countUni += arrUnigram.Length;
                countBi += arrUnigram.Length - 1;
                Dictionary<string, float> dicUniFreqlist;
                Dictionary<string, float> dicBiFreqlist;
                // cộng dồn unigram và bigram đối với các lớp ngữ nghĩa đã tồn tại
                if (pug.TryGetValue(g, out dicUniFreqlist))
                {
                    dicBiFreqlist = pbg[g];
                    int count = 0;
                    string preUnigram = string.Empty;
                    foreach (string unigram in arrUnigram)
                    {
                        float val;
                        if (!stoplist.Contains(unigram))
                        {
                            if (dicUniFreqlist.TryGetValue(unigram, out val))
                                dicUniFreqlist[unigram] = val + 1;
                            else
                                dicUniFreqlist.Add(unigram, 1);
                        }
                        if (count > 0)
                        {
                            string bigram = preUnigram + '_' + unigram;
                            if (dicBiFreqlist.TryGetValue(bigram, out val))
                                dicBiFreqlist[bigram] = val + 1;
                            else
                                dicBiFreqlist.Add(bigram, 1);
                        }
                        preUnigram = unigram;
                        ++count;
                    }
                    pug[g] = dicUniFreqlist;
                    pbg[g] = dicBiFreqlist;
                }
                // thêm lớp ngữ nghĩa mới
                else
                {
                    dicUniFreqlist = new Dictionary<string, float>();
                    dicBiFreqlist = new Dictionary<string, float>();
                    string preUnigram = string.Empty;
                    int count = 0;
                    foreach (string unigram in arrUnigram)
                    {
                        float val;
                        if (!stoplist.Contains(unigram))
                        {
                            if (dicUniFreqlist.TryGetValue(unigram, out val))
                                dicUniFreqlist[unigram] += 1;
                            else
                                dicUniFreqlist.Add(unigram, 1);
                        }
                        if (count > 0)
                        {
                            string bigram = preUnigram + '_' + unigram;
                            if (dicBiFreqlist.TryGetValue(bigram, out val))
                                dicBiFreqlist[bigram] += 1;
                            else
                                dicBiFreqlist.Add(bigram, 1);
                        }
                        preUnigram = unigram;
                        ++count;
                    }
                    pug.Add(g, dicUniFreqlist);
                    pbg.Add(g, dicBiFreqlist);
                }
            }

            List<string> lstG;
            lstG = new List<string>(pug.Keys);
            foreach (string g in lstG)
            {
                List<string> lstUnigram = new List<string>(pug[g].Keys);
                foreach (string unigram in lstUnigram)
                    pug[g][unigram] /= countUni;
            }
            lstG = new List<string>(pbg.Keys);
            foreach (string g in lstG)
            {
                List<string> lstBigram = new List<string>(pbg[g].Keys);
                foreach (string bigram in lstBigram)
                    pbg[g][bigram] /= countBi;
            }
        }

        /// <summary>
        /// Kiểm tra tính hội tụ của kết quả
        /// Phương pháp 1: dựa trên sai khác về kiểu số thực
        /// </summary>
        /// <param name="pre_plist"></param>
        /// <param name="plist"></param>
        /// <param name="checkfile"></param>
        /// <param name="threshold"></param>
        /// <param name="converge"></param>
        /// <returns></returns>
        public static Dictionary<string, float> Review1(Dictionary<string, float> pre_plist, Dictionary<string, float> plist, string checkfile, float threshold, out bool converge)
        {
            Dictionary<string, float> dicNewPList = new Dictionary<string, float>();
            converge = true;
            int count = 0;
            using (StreamWriter sw = new StreamWriter(checkfile.Insert(14, DateTime.Now.ToString("_ddMMyyyy_HHmmss")).Insert(5, "bst-check/")))
            {
                sw.WriteLine("----- REMAINING -----");
                foreach (var key in plist.Keys)
                {
                    float diff = plist[key] - pre_plist[key];
                    if (diff < -threshold || diff > threshold)
                    {
                        converge = false;
                        sw.WriteLine(key);
                        ++count;
                    }
                    if (!_customConverge)
                        dicNewPList.Add(key, plist[key] - (diff / 2));
                    else
                        dicNewPList.Add(key, plist[key]);
                }
                sw.WriteLine();
            }
            if (((float)count / plist.Count) >= threshold)
                _customConverge = true;
            else
                _customConverge = false;
            Console.WriteLine("Remaining unconverged: " + count);
            return dicNewPList;
        }

        /// <summary>
        /// Kiểm tra tính hội tụ của kết quả
        /// Phương pháp 2: dựa trên sai khác về phần trăm
        /// </summary>
        /// <param name="pre_plist"></param>
        /// <param name="plist"></param>
        /// <param name="checkfile"></param>
        /// <param name="threshold"></param>
        /// <param name="converge"></param>
        /// <returns></returns>
        public static Dictionary<string, float> Review2(Dictionary<string, float> pre_plist, Dictionary<string, float> plist, string checkfile, float threshold, out bool converge)
        {
            Dictionary<string, float> dicNewPList = new Dictionary<string, float>();
            converge = true;
            int total = plist.Count;
            int remaining = 0;
            //using (StreamWriter sw = File.AppendText(checkfile))
            using (StreamWriter sw = new StreamWriter("bstcheck/" + DateTime.Now.ToShortTimeString() + "_" + checkfile))
            {
                sw.WriteLine("----- REMAINING -----");
                foreach (var key in plist.Keys)
                {
                    float diff = (plist[key] - pre_plist[key]) / pre_plist[key];
                    if (diff < -threshold || diff > threshold)
                    {
                        converge = false;
                        sw.WriteLine(key);
                        ++remaining;
                    }
                    dicNewPList.Add(key, plist[key] - (diff / 2));
                }
                sw.WriteLine();
            }
            Console.WriteLine("Remaining unconverged: " + remaining);
            return dicNewPList;
        }

        /// <summary>
        /// Kiểm tra số nghĩa tiếng Việt của từ tiếng Anh (để chọn ví dụ cho báo cáo)
        /// </summary>
        /// <param name="file"></param>
        private static void CheckMaxTranslations(string file)
        {
            int max = 0;
            string en = string.Empty;
            XElement root = XElement.Load(file);
            foreach (XElement node in root.Elements())
            {
                List<string> lst = new List<string>();
                foreach (XElement subNode in node.Elements())
                {
                    string[] args = subNode.Value.Split(':');
                    if (!lst.Contains(args[0]))
                        lst.Add(args[0]);
                }
                if (lst.Count >= max)
                {
                    max = lst.Count;
                    en = node.FirstAttribute.Value;
                }
            }
            Console.WriteLine(en);
            Console.WriteLine(max);
        }
    }
}